package cn.jinbyte.starter.web.autoconfigure;


import cn.jinbyte.core.handler.crypt.CryptHandler;
import cn.jinbyte.core.handler.crypt.Sm4CryptHandler;
import cn.jinbyte.starter.web.config.ApiLogConfig;
import cn.jinbyte.starter.web.config.WebLogProperties;
import cn.jinbyte.starter.web.config.StarterConst;
import cn.jinbyte.starter.web.config.WebSecurityProperties;
import cn.jinbyte.web.aspect.LoginLogAspect;
import cn.jinbyte.web.dao.ApiLogDao;
import cn.jinbyte.web.dao.LoginLogDao;
import cn.jinbyte.web.filter.*;
import cn.jinbyte.web.exception.GlobalErrorHandler;
import cn.jinbyte.web.resolver.CryptPropArgResolver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;

import static cn.jinbyte.starter.web.config.StarterConst.CONF_PREFIX;

/**
 * 安全防护自动配置类
 * 当Spring检测到DispatcherServlet存在时自动生效
 */
@Slf4j
@AutoConfiguration
@EnableConfigurationProperties({
        WebSecurityProperties.class,
        WebLogProperties.class
})
@SuppressWarnings("unused")
public class WebAutoConfiguration {
    /**
     * XSS过滤器自动配置
     */
    @Bean
    @ConditionalOnMissingBean(XssFilter.class)
    @ConditionalOnProperty(prefix = CONF_PREFIX + ".xss", name = "enabled", havingValue = "true", matchIfMissing = true)
    public FilterRegistrationBean<XssFilter> xssFilter(WebSecurityProperties properties) {
        log.info("XSS过滤器已启用");
        FilterRegistrationBean<XssFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new XssFilter(properties.getXss()));
        registrationBean.setOrder(1);
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    /**
     * SQL注入过滤器自动配置
     */
    @Bean
    @ConditionalOnMissingBean(SqlInjectionFilter.class)
    @ConditionalOnProperty(prefix = CONF_PREFIX + ".sql-injection", name = "enabled", havingValue = "true", matchIfMissing = true)
    public FilterRegistrationBean<SqlInjectionFilter> sqlInjectionFilter(WebSecurityProperties properties) {
        log.info("SQL注入过滤器已启用");
        FilterRegistrationBean<SqlInjectionFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new SqlInjectionFilter(properties.getSqlInjection()));
        registrationBean.setOrder(2);
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    /**
     * CORS过滤器自动配置
     */
    @Bean
    @ConditionalOnMissingBean(CorsFilter.class)
    @ConditionalOnProperty(prefix = CONF_PREFIX + ".cors", name = "enabled", havingValue = "true", matchIfMissing = true)
    public FilterRegistrationBean<CorsFilter> corsFilter(WebSecurityProperties properties) {
        log.info("CORS过滤器已启用");
        FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new CorsFilter(properties.getCors()));
        registrationBean.setOrder(3);
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    /**
     * CSRF过滤器自动配置
     */
    @Bean
    @ConditionalOnMissingBean(CsrfFilter.class)
    @ConditionalOnProperty(prefix = CONF_PREFIX + ".csrf", name = "enabled", havingValue = "true", matchIfMissing = true)
    public FilterRegistrationBean<CsrfFilter> csrfFilter(WebSecurityProperties properties) {
        log.info("CSRF过滤器已启用");
        FilterRegistrationBean<CsrfFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new CsrfFilter(properties.getCsrf()));
        registrationBean.setOrder(4);
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    /**
     * 安全响应头过滤器自动配置
     */
    @Bean
    @ConditionalOnMissingBean(SecurityHeadersFilter.class)
    @ConditionalOnProperty(prefix = CONF_PREFIX + ".headers", name = "enabled", havingValue = "true", matchIfMissing = true)
    public FilterRegistrationBean<SecurityHeadersFilter> securityHeadersFilter(WebSecurityProperties properties) {
        log.info("安全响应头过滤器已启用");
        FilterRegistrationBean<SecurityHeadersFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new SecurityHeadersFilter(properties.getHeaders()));
        registrationBean.setOrder(5);
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    /**
     * 配置默认的加解密处理器
     */
    @Bean("DefaultCryptHandler")
    public CryptHandler cryptHandler(WebSecurityProperties properties) {
        return new Sm4CryptHandler(properties.getArgCryptKey());
    }

    /**
     * 加密参数解析器自动配置
     */
    @Bean
    @ConditionalOnMissingBean
    public CryptPropArgResolver cryptArgResolver() {
        log.info("加密参数解析器已启用");
        return new CryptPropArgResolver();
    }

    /**
     * 全局异常处理器自动配置
     */
    @Bean
    @ConditionalOnMissingBean
    public GlobalErrorHandler globalErrorHandler() {
        log.info("全局异常处理器已启用");
        return new GlobalErrorHandler();
    }


    /**
     * 接口日志配置
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = StarterConst.CONF_PREFIX + ".api-log", name = "enabled", havingValue = "true", matchIfMissing = true)
    public ApiLogConfig apiLogConfig(WebLogProperties properties, ApiLogDao logDao) {
        log.info("接口日志已启用");
        return new ApiLogConfig(properties.getApiLog(), logDao);
    }

    /**
     * 登录日志切面
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = StarterConst.CONF_PREFIX + ".login-log", name = "enabled", havingValue = "true", matchIfMissing = true)
    public LoginLogAspect loginLogAspect(WebLogProperties properties, LoginLogDao logDao) {
        log.info("登录日志已启用");
        return new LoginLogAspect(properties.getLoginLog(), logDao);
    }
}
